package com.love.cloud.common.core.util;

import cn.hutool.core.util.ObjectUtil;
import com.love.cloud.common.core.interfaces.TreeSupport;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * Description: <br/>
 * date: 2022/2/23 9:41<br/>
 *
 * @author wangzhiqiong <br/>
 */
public class MyTreeUtil {

    /**
     * 存在直接父子关系的数据转成树形结构
     * @param nodes 节点
     * @param <T>
     * @return
     */
    public static <T extends TreeSupport<T,R>, R> List<T> toTree(List<T> nodes) {
        // key :父节点， value:子节点
        HashMap<R, Collection<T>> childrenMap = new HashMap<>();
        // key: 节点id， value:对应的节点
        Map<R, T> nodeMap = new HashMap<>();
        List<T> targetNodes = new ArrayList<>();
        for (T node : nodes) {
            R id = node.getId();
            R parentId = node.getParentId();
            if(!childrenMap.containsKey(parentId)) {
                childrenMap.put(parentId, new ArrayList<>());
            }
            childrenMap.get(parentId).add(node);
            nodeMap.put(id, node);
        }
        List<T> targetList = new ArrayList<>();
        nodeMap.forEach((nodeId, node) -> {
            Collection<T> children = childrenMap.get(nodeId);
            node.setChildren(children);
            R parentId = node.getParentId();
            if(!nodeMap.containsKey(parentId)) {
                targetList.add(node);
            }
        });
        return targetList;
    }

    /**
     * 存在树形关系的数据转成树形结构，允许跨级，子节点会挂靠到离他最近的父节点上
     *  A1-A2-A3, B1-B2-B3
     *  如果传入的nodes是，[A1,B2,B1,A3] -> A1->A3, B2->B3 ,即允许跨级
     * @param nodes 节点
     * @param splitFlag 路径的分隔符，例如部门的路径分割符是/
     * @param <T>
     * @return
     */
    public static <T extends TreeSupport<T,K>, K> List<T> toTree(List<T> nodes, String splitFlag) {

        // key: 节点id， value:对应的节点
        Map<String, T> nodeMap = new HashMap<>();

        for (T node : nodes) {
            K id = node.getId();
            nodeMap.put(id.toString(), node);
        }

        // key:节点id， value:最近的父节点id
        Map<String, String> childrenId2NearestParentNodeMap = new HashMap<>();
        for (T node : nodes) {
            String id = node.getId().toString();
            String path = node.getPath();
            Set<String> parentIdsSet = new HashSet<>();
            Collections.addAll(parentIdsSet, path.split(splitFlag));
            for (String parentId : parentIdsSet) {
                // 路径上会包含当前节点，所以跳过当前节点
                if(StringUtils.isBlank(parentId) || parentId.equals(id)) {
                    continue;
                }
                // 如果该父节点不在查询结果中，则跳过
                if(!nodeMap.containsKey(parentId)) {
                    continue;
                }
                if(!childrenId2NearestParentNodeMap.containsKey(id)) {
                    childrenId2NearestParentNodeMap.put(id, parentId);
                    continue;
                }
                // minParent 目前的最近的父节点 thisParent 当前parentId对应节点
                String oldMinParentId = childrenId2NearestParentNodeMap.get(id);
                T minParent = nodeMap.get(oldMinParentId);
                T thisParent = nodeMap.get(parentId);
                if(minParent == null || thisParent == null) {
                    continue;
                }
                // 如果thisParent 是比minParent更近的父节点，则替换最近节点
                if(thisParent.getPath().length() > minParent.getPath().length()) {
                    childrenId2NearestParentNodeMap.put(id, parentId);
                }
            }
        }
        // key :最近的父节点id， value:子节点
        HashMap<String, Collection<T>> childrenMap = new HashMap<>();

        for (T node : nodes) {
            String id = node.getId().toString();
            // 获取最近的父节点
            String parentId = childrenId2NearestParentNodeMap.get(id);
            if(!childrenMap.containsKey(parentId)) {
                childrenMap.put(parentId, new ArrayList<>());
            }
            childrenMap.get(parentId).add(node);
        }

        List<T> targetList = new ArrayList<>();
        nodeMap.forEach((nodeId, node) -> {
            Collection<T> children = childrenMap.get(nodeId);
            node.setChildren(children);

            // 获取最近的父节点
            String parentId = childrenId2NearestParentNodeMap.get(nodeId);
            if(!nodeMap.containsKey(parentId)) {
                targetList.add(node);
            }
        });
        return targetList;
    }

    public static <T extends TreeSupport> void fullTreeDepth(T root, Collection<T> children, Integer treeDepth) {
        if(CollectionUtils.isEmpty(children)) {
            root.setMaxTreeDepth(ObjectUtil.defaultIfNull(root.getMaxTreeDepth(), treeDepth));
            return;
        }
        treeDepth++;
        for (T child : children) {
            child.setTreeDepth(treeDepth);
            if(root.getMaxTreeDepth() == null) {
                root.setMaxTreeDepth(treeDepth);
            }
            if(root.getDeepestChildNode() == null) {
                root.setDeepestChildNode(child);
            }
            if(treeDepth > root.getMaxTreeDepth()) {
                root.setMaxTreeDepth(treeDepth);
                root.setDeepestChildNode(child);
            }
            fullTreeDepth(root, child.getChildren(), treeDepth);
        }

        return;
    }


    public static <T extends TreeSupport> int calcTreeDepth(T node, Integer level) {
        if(node == null || CollectionUtils.isEmpty(node.getChildren())) {
            return level;
        }
        level ++;
        int maxLevel = 0;
        Collection<T> children = node.getChildren();
        for (T child : children) {
            int currentLevel = calcTreeDepth(child, level);
            if(currentLevel > maxLevel) {
                maxLevel = currentLevel;
            }
        }
        return maxLevel;
    }

    public static  <T extends TreeSupport> void fullDivision(Collection<T> deptTrees, String division) {
        if(CollectionUtils.isEmpty(deptTrees)) {
            return;
        }
        for (T deptTree : deptTrees) {
            String thisDivision = ObjectUtil.defaultIfNull(deptTree.getDivision(),division);
            deptTree.setDivision(thisDivision);
            fullDivision(deptTree.getChildren(), thisDivision);
        }
    }
}
